package server

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"os"
	"src/gcommon"
	"src/server/Login/logindbmanager"
	"src/zeus/common"
	"src/zeus/dbservice"
	"src/zeus/etcd"
	"src/zeus/iserver"
	"src/zeus/logger"
	server2 "src/zeus/server"
	"src/zeus/serverMgr"
	"src/zeus/threads"
	"strings"

	"github.com/antlinker/go-dirtyfilter/store"
	"github.com/buguang01/util/flags"
	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
)

var (
	singleServer *Server
)

type SDKConfig struct {
	APPID     string // 本游戏在SDK中的唯一标识
	AppSecret string // 本游戏在SDK中的密钥
	Addr      string // 对应SDK的地址
}

// Server 登陆服务器
type Server struct {
	// *login.App

	//地址
	addr string
	//端口
	port  string
	SrvID uint64
	// 0
	sGroupID uint32

	VersionCheck bool   //是否进行版本检查
	MinVersion   string //最小可用的版本

	LoginSrvVerifyToken string // 服务器之间的Token

	memStore  *store.MemoryStore
	GinEngine *gin.Engine //HTTP的路由器

	Tgo   *threads.ThreadGo
	Gener iserver.IUIDGenerater
}

// NewServer 创建一个新的登陆服务器
// 帧率为20帧
func GetServer() *Server {
	if singleServer == nil {
		srv := new(Server)
		srv.Tgo = threads.NewThreadGo()

		gin.DefaultWriter = logger.NewLoggerIO(logger.LogLevelDebug, logger.LogKey_GinHttp)
		gin.DefaultErrorWriter = logger.NewLoggerIO(logger.LogLevelError, logger.LogKey_GinHttp)
		// gin.SetMode(gin.ReleaseMode)
		servername := flags.GetFlagByString(common.ServerName)
		vmap := viper.New()
		vmap.MergeConfigMap(viper.GetStringMap(servername))
		srv.addr = viper.GetString("Login.Listen")
		srv.port = fmt.Sprint(viper.GetInt("Login.PortMin") + flags.GetFlagByInt(common.ServerNumber))
		srv.SrvID = uint64(vmap.GetInt64("FlagId")) + uint64(flags.GetFlagByInt(common.ServerNumber))

		connectRedises := viper.GetStringSlice("Login.ConnectRedises")
		dbservice.InitRedises(connectRedises)
		gcommon.InitMsg()
		singleServer = srv
	}

	return singleServer
}

func (s *Server) SetRouter(router *gin.Engine) {
	s.GinEngine = router
}

// Run 启动
func (s *Server) Run() {
	logindbmanager.GetInst()
	/*
		这里还要加点调试判断
	*/
	if gcommon.GetCfgInst().Config.IsCreateDB {
		logindbmanager.GetInst().AutoMigrate()
	}

	etcd.GetEtcd().Start()
	if s.GinEngine == nil {
		s.GinEngine = gin.New()
	}
	s.Tgo.Go(func(ctx context.Context) {
		httpSrv := &http.Server{
			Addr:    s.addr + ":" + s.port,
			Handler: s.GinEngine,
		}

		s.Tgo.Go(func(ctx context.Context) {
			//如果收到退出信息就关了连接
			select {
			case <-ctx.Done():
				httpSrv.Shutdown(context.Background())
			}
		})
		logger.Info("http server will listen: ", httpSrv.Addr)
		// err = s.GinEngine.RunListener(l)
		err := httpSrv.ListenAndServe()
		if err != nil {
			if !strings.Contains(err.Error(), "use of closed network connection") && !errors.Is(err, http.ErrServerClosed) {
				//panic(err)
				logger.Error("http server listen error: ", err)
				logger.Flush()
				os.Exit(201) //重大异常 暴力退出
			} else {
				logger.Info("http server normal closed: ", err)
			}
		}

	})
	/*
		因为在
		go s.IServer.Run()
		里面也会运行
		serverMgr.InitServerMgr(s.sGroupID, serverTeypeList)
		导致异步初始化2次，出来内部的数据没有初始化完成，导致没有启动成功
		所以把初始化的逻辑放在了主协程上，在完成后，再运行
		go s.IServer.Run()
	*/
	duration := viper.GetDuration("Login.RefreshDuration")
	s.sGroupID = 0 //uint32(viper.GetInt("Config.GroupId"))
	if duration == 0 {
		duration = 10
	}
	s.Gener = server2.NewIDGenerater(s.SrvID)

	//为了方便调用GetNowTime() 和一些底层调用iserver.GetSrvInst()的地方不会出问题;最小化实现,请勿滥用
	iserver.SetSrvInst(&iserverNotImpl{Server: s})

	return
}

// Stop 关闭LoginServer
func (s *Server) Stop() {
	s.Tgo.CloseWait()
	logger.Info("Login Stop.")
}

// func (s *Server) loadConfig() {
// 	// s.msdkAddr = viper.GetString("Config.MSDKAddr")
// 	// s.AppID = viper.GetString("Config.AppID")
// 	// s.AppSecret = viper.GetString("Config.AppSecret")

// 	// s.VersionCheck = viper.GetBool("Config.VersionCheck")
// 	// s.Config = &symbol.LoginConfig{}
// 	// s.Config.HBEnable = viper.GetBool("Config.HeartBeat")

// 	// logger.Info("Load Config:")
// 	// logger.Info("Config:", s.Config)
// }

// GetGatewayAddr 获取网关信息
func (app *Server) GetGatewayAddr(sGroupId uint32) (string, error) {
	//TODO 网关信息的更新机制
	// app.GlobalEvents.HandleEvent()

	gateway, err := serverMgr.GetServerMgr().GetGatewayServerInfoByGroup(sGroupId)
	if err != nil {
		return "", err
	}
	return gateway.OuterAddress, nil
}

func (app *Server) GetGatewayHttpAddr(sGroupId uint32) (string, error) {
	//TODO 网关信息的更新机制
	// app.GlobalEvents.HandleEvent()

	gateway, err := serverMgr.GetServerMgr().GetGatewayServerInfoByGroup(sGroupId)
	if err != nil {
		return "", err
	}
	return gateway.HttpAddress, nil
}
